#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include "driver/i2s_std.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_check.h"
#include "sdkconfig.h"
#include "keysound.h"


#define PLAY BIT0       // 播放
#define IDEL BIT1       // 空闲
#define PLAY_MUSIC BIT3 // 播放音乐
#define STOP_MUSIC BIT4 // 播放音乐
#define UNMUTE BIT5       // 按键音打开

// 按键音
extern const uint8_t music_pcm_start[] asm("_binary_canon_pcm_start");
extern const uint8_t music_pcm_end[]   asm("_binary_canon_pcm_end");

// 音乐
extern const uint8_t music_nowbf_pcm_start[] asm("_binary_nowbf_pcm_start");
extern const uint8_t music_nowbf_pcm_end[]   asm("_binary_nowbf_pcm_end");

static const char *TAG = "MAX98357A";
static const char err_reason[][30] = {"input param is invalid",
                                      "operation timeout"
                                     };
i2s_chan_handle_t tx_handle;
i2s_chan_handle_t rx_handle;

uint16_t desc_buf[256];
size_t len;
int inc;


static EventBits_t ev;   //事件位中间变量
//事件组句柄
static EventGroupHandle_t s_testEvent;

static void i2s_music(void)
{
    esp_err_t ret = ESP_OK;
    size_t bytes_write = 0;
    uint8_t *data_ptr = (uint8_t *)music_pcm_start;
    size_t total_bytes = music_pcm_end - data_ptr;  // 音乐总长度
    size_t bytes_per_chunk = 256;  // 每次写入的数据量

    ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
    ESP_ERROR_CHECK(i2s_channel_preload_data(tx_handle, data_ptr, music_pcm_end - data_ptr, &bytes_write));
    data_ptr += bytes_write;  // Move forward the data pointer
	total_bytes -= bytes_write;

    bool keepPlay = true;
    /* Enable the TX channel */
    while (1) {
        //pdFALSE 
        ev = xEventGroupWaitBits(s_testEvent, PLAY|PLAY_MUSIC, pdTRUE, pdFALSE, portMAX_DELAY);
        if (ev & PLAY) {
            data_ptr = (uint8_t *)music_pcm_start;
            total_bytes = music_pcm_end - data_ptr;  // 音乐总长度
            bytes_write = 0;
            xEventGroupClearBits(s_testEvent, PLAY);
            ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
            while (total_bytes > 0) {
                ev = xEventGroupWaitBits(s_testEvent, PLAY, pdTRUE, pdFALSE, 0);
                if (ev & PLAY) {
                    data_ptr = (uint8_t *)music_pcm_start;
                    total_bytes = music_pcm_end - data_ptr;  // 音乐总长度
                    bytes_write = 0;
                    break;
                }
                size_t bytes_to_write = (total_bytes > bytes_per_chunk) ? bytes_per_chunk : total_bytes;
                // ESP_LOGE(TAG, "=========================> i2s_channel_write ...");
                ret = i2s_channel_write(tx_handle, data_ptr, bytes_to_write, &bytes_write, portMAX_DELAY);
                // ESP_LOGE(TAG, "=========================> after i2s_channel_write ...");
                if (ret != ESP_OK) {
                    ESP_LOGE(TAG, "Failed to write to I2S: %s", err_reason[ret == ESP_ERR_TIMEOUT]);
                    abort();
                }
                data_ptr += bytes_write;  // 更新数据指针
                total_bytes -= bytes_write;  // 更新剩余数据量
                // vTaskDelay(10);
            }
            ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
            // data_ptr = (uint8_t *)music_pcm_start;
            // total_bytes = music_pcm_end - data_ptr;  // 音乐总长度
            // bytes_write = 0;
            // vTaskDelay(1000 / portTICK_PERIOD_MS);
        } else if (ev & PLAY_MUSIC) {
            keepPlay = true;
            xEventGroupClearBits(s_testEvent, PLAY_MUSIC);
            ESP_LOGE(TAG, "playing music ...");
            data_ptr = (uint8_t *)music_nowbf_pcm_start;
            total_bytes = music_nowbf_pcm_end - data_ptr;  // 音乐总长度
            bytes_write = 0;
            while (keepPlay)
            {
                ev = xEventGroupWaitBits(s_testEvent, STOP_MUSIC, pdTRUE, pdFALSE, 0);
                if (ev & STOP_MUSIC) {
                    xEventGroupClearBits(s_testEvent, PLAY_MUSIC);
                    xEventGroupClearBits(s_testEvent, STOP_MUSIC);
                    data_ptr = (uint8_t *)music_pcm_start;
                    total_bytes = music_pcm_end - data_ptr;  // 音乐总长度
                    bytes_write = 0;
                    keepPlay = false;
                    break;
                }
                ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
                while (total_bytes > 0) {
                    ev = xEventGroupWaitBits(s_testEvent, STOP_MUSIC, pdTRUE, pdFALSE, 0);
                    if (ev & STOP_MUSIC) {
                        xEventGroupClearBits(s_testEvent, PLAY_MUSIC);
                        xEventGroupClearBits(s_testEvent, STOP_MUSIC);
                        keepPlay = false;
                        break;
                    }
                    size_t bytes_to_write = (total_bytes > bytes_per_chunk) ? bytes_per_chunk : total_bytes;
                    // ESP_LOGE(TAG, "=========================> i2s_channel_write ...");
                    ret = i2s_channel_write(tx_handle, data_ptr, bytes_to_write, &bytes_write, portMAX_DELAY);
                    // ESP_LOGE(TAG, "=========================> after i2s_channel_write ...");
                    if (ret != ESP_OK) {
                        ESP_LOGE(TAG, "Failed to write to I2S: %s", err_reason[ret == ESP_ERR_TIMEOUT]);
                        abort();
                    }
                    data_ptr += bytes_write;  // 更新数据指针
                    total_bytes -= bytes_write;  // 更新剩余数据量
                }
                ESP_ERROR_CHECK(i2s_channel_disable(tx_handle));
                data_ptr = (uint8_t *)music_nowbf_pcm_start;
                total_bytes = music_nowbf_pcm_end - data_ptr;  // 音乐总长度重新赋值，不会退出循环
                bytes_write = 0;
            }
        }
    }
	i2s_channel_disable(rx_handle);
	i2s_del_channel(rx_handle);
    vTaskDelete(NULL);
}

void keysound_task(void *arg) {
	i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
	i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle);
	i2s_std_config_t std_cfg = {
	    .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
	    .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
	    .gpio_cfg = {
	        .mclk = I2S_GPIO_UNUSED,
	        .bclk = GPIO_NUM_39,
	        .ws = GPIO_NUM_40,   // LRC
	        .dout = GPIO_NUM_38, // 
	        .din = -1,  // unused
	        .invert_flags = {
	            .mclk_inv = false,
	            .bclk_inv = false,
	            .ws_inv = false,
	        },
	    },
	};
	i2s_channel_init_std_mode(tx_handle, &std_cfg);
	i2s_channel_init_std_mode(rx_handle, &std_cfg);
	
	i2s_channel_enable(tx_handle);
	i2s_channel_enable(rx_handle);
	
	i2s_music();
}

void init_keysound(void)
{
    s_testEvent = xEventGroupCreate();
    xEventGroupSetBits(s_testEvent, UNMUTE);
    xEventGroupSetBits(s_testEvent, IDEL);
    xTaskCreatePinnedToCore(keysound_task, "keysound_task", 2048*2, NULL,2, NULL, 1);
}

bool startPlay(void) {
    ev = xEventGroupWaitBits(s_testEvent, UNMUTE, pdFALSE, pdFALSE, 0);
    if (ev & UNMUTE) {
        xEventGroupSetBits(s_testEvent, PLAY);      
    }
    return true;
}

bool startPlayMusic(void) {
    xEventGroupClearBits(s_testEvent, STOP_MUSIC);
    xEventGroupSetBits(s_testEvent, PLAY_MUSIC);
    return true;
}

bool stopPlayMusic(void) {
    xEventGroupSetBits(s_testEvent, STOP_MUSIC);
    return true;
}

void setKeySound(bool enable) {
    if (enable) {
        xEventGroupSetBits(s_testEvent, UNMUTE);
    } else {
        xEventGroupClearBits(s_testEvent, UNMUTE);
    }
}

bool isKeySound(void){
    ev = xEventGroupWaitBits(s_testEvent, UNMUTE, pdFALSE, pdFALSE, 0);
    if (ev & UNMUTE) {
        return true;     
    } else {
        return false;
    }
}
